import 'dart:async';

import 'package:flutter/material.dart';
import 'package:xws/helpers/as_t.dart';
import 'package:xws/helpers/safe_state.dart';
import 'package:xws/helpers/safe_value_notifier.dart';
import 'package:xws/models/notify/chat_msg_obj.dart';
import 'package:xws/models/notify/chat_obj.dart';
import 'package:xws/models/notify/jpush_obj.dart';
import 'package:xws/res/icons.dart';
import 'package:xws/state/app_state.dart';
import 'package:xws/state/user_state.dart';
import 'package:xws/utils/dio_utils/qiniu_oss.dart';
import 'package:xws/utils/dio_utils/request/user_req.dart';
import 'package:xws/utils/log_util.dart';
import 'package:xws/utils/push_util.dart';
import 'package:xws/utils/theme_util.dart';
import 'package:xws/utils/tools_util.dart';
import 'package:xws/widgets/app_btn.dart';
import 'package:xws/widgets/app_text_field.dart';
import 'package:xws/widgets/ex_value_builder.dart';
import 'package:xws/widgets/loading_icon.dart';
import 'package:xws/widgets/refresh_widget/loading_view.dart';
import 'package:xws/widgets/safe_scaffold.dart';
import 'package:xws/widgets/slide_callback.dart';

import 'goods_box.dart';
import 'msg_item.dart';

/// 聊天状态
class _ChatState {
  _ChatState({
    required this.contentIsText,
    required this.showGoods,
    required this.isSending,
    required this.unreadCount,
  });

  factory _ChatState.init() => _ChatState(
        contentIsText: false,
        showGoods: true,
        isSending: false,
        unreadCount: 0,
      );

  final bool contentIsText;
  final bool showGoods;
  final bool isSending;
  final int unreadCount;

  _ChatState copy({
    bool? contentIsText,
    bool? showGoods,
    bool? isSending,
    int? unreadCount,
  }) {
    return _ChatState(
      contentIsText: contentIsText ?? this.contentIsText,
      showGoods: showGoods ?? this.showGoods,
      isSending: isSending ?? this.isSending,
      unreadCount: unreadCount ?? this.unreadCount,
    );
  }
}

/// 聊天界面
class ChatPage extends StatefulWidget {
  const ChatPage({
    Key? key,
    required this.chatObj,
    this.unreadCallback,
  }) : super(key: key);

  @override
  _ChatPageState createState() => _ChatPageState();

  final ChatObj chatObj;
  final void Function(int)? unreadCallback;
}

class _ChatPageState extends State<ChatPage> with SafeState<ChatPage> {
  late TextEditingController _textEditingController;

  late SafeValueNotifier<_ChatState> _state;

  late ScrollController _controller;

  late FocusNode _focusNode;

  final GlobalKey _centerKey = GlobalKey();
  final GlobalKey _newChatKey = GlobalKey();

  final List<ChatMegObj> _newData = <ChatMegObj>[];
  final List<ChatMegObj> _loadData = <ChatMegObj>[];

  int _page = 1;

  bool _isInit = false;
  bool _isNoMore = false;

  bool _showGoods = true;

  Color get _bgColor => canvas(context).darken(0.02);

  @override
  void initState() {
    super.initState();
    _textEditingController = TextEditingController();
    _state = SafeValueNotifier<_ChatState>(_ChatState.init());
    _controller = ScrollController();
    _focusNode = FocusNode();
    PU.putHandler(_receive);

    _init();
  }

  @override
  void dispose() {
    _textEditingController.dispose();
    _state.dispose();
    _controller.dispose();
    _focusNode.dispose();

    PU.popHandler();

    super.dispose();
  }

  /// 刷新
  Future<void> _init() async {
    _page = 1;
    _isNoMore = false;

    final List<ChatMegObj> temp = await chatData(
      pageNum: _page,
      conversationId: widget.chatObj.id,
    );

    if (temp.isNotEmpty) {
      _loadData.clear();
      _newData.clear();
      _loadData.addAll(temp);
    }

    await delayed(const Duration(milliseconds: 300));

    safeSetState(() => _isInit = true);
  }

  /// 加载
  Future<void> _onLoad() async {
    _page++;
    final List<ChatMegObj> temp = await chatData(
      pageNum: _page,
      conversationId: widget.chatObj.id,
    );

    if (temp.isEmpty) {
      _page--;
      if (!_isNoMore) safeSetState(() => _isNoMore = true);
    } else {
      _loadData.addAll(temp);
      await delayed(const Duration(milliseconds: 300));
      safeSetState(() {});
    }
  }

  /// 发送消息
  Future<void> _send(bool isText) async {
    if (isText) {
      if (_textEditingController.text.trim().isEmpty) return;
    }

    if (_state.value.isSending) return;

    _state.value = _state.value.copy(isSending: true);

    final DateTime now = DateTime.now();
    final String create = now.toString().split('.').first.replaceFirst('from', 'T');

    if (isText) {
      final bool? r = await setMsg(
        content: _textEditingController.text,
        conversationId: widget.chatObj.id,
      );

      if (r ?? false) {
        _newData.add(
          ChatMegObj.fromJson(<String, dynamic>{
            'userId': us.info.id,
            'content': _textEditingController.text,
            'messageType': 'TEXE',
            'createdAt': create,
          }),
        );
        safeSetState(() {});
        _textEditingController.clear();
        await delayed(const Duration(milliseconds: 300));
      }
    } else {
      final String? img = (await pickImgs(context))?.single;
      if (img == null) {
        _state.value = _state.value.copy(isSending: false);
        return;
      }

      final List<String> imgList = await QiniuOSS.uploadFiles(<String>[img], Dir.chat);
      if (imgList.isEmpty) {
        _state.value = _state.value.copy(isSending: false);
        return;
      }

      final bool? r = await setMsg(
        content: imgList.single,
        conversationId: widget.chatObj.id,
        msgType: MsgType.IMAGE,
      );

      if (r ?? false) {
        _newData.add(
          ChatMegObj.fromJson(<String, dynamic>{
            'id': now.millisecond.toString(),
            'userId': us.info.id,
            'content': imgList.single,
            'messageType': 'IMAGE',
            'createdAt': create,
          }),
        );
        safeSetState(() {});
        await delayed(const Duration(milliseconds: 500));
      }
    }

    _state.value = _state.value.copy(isSending: false, contentIsText: false);

    _scrollToEnd();
  }

  /// 收到消息
  Future<void> _receive(Map<String, dynamic> event) async {
    try {
      if (event.containsKey('extras')) {
        final JPushObj pushObj = JPushObj.fromJson(event);

        if (pushObj.getExtras['conversationId'] == widget.chatObj.id) {
          PU.cancelJPushNotify(clearAll: true);

          final DateTime now = DateTime.now();
          final String create = now.toString().split('.').first.replaceFirst('from', 'T');

          final String msgId = asT<String>(pushObj.getExtras['messageId']);

          final int inNewData = _newData.indexWhere((ChatMegObj c) => c.id == msgId);
          final int inloadData = _loadData.indexWhere((ChatMegObj c) => c.id == msgId);

          if (inNewData < 0 && inloadData < 0) {
            _newData.add(
              ChatMegObj.fromJson(<String, dynamic>{
                'id': msgId,
                'userId': widget.chatObj.fromUserId,
                'content': pushObj.getExtras['content'],
                'messageType': pushObj.getExtras['messageType'],
                'createdAt': create,
              }),
            );

            safeSetState(() {});

            await delayed(const Duration(milliseconds: 300));

            if (_controller.offset < _controller.position.minScrollExtent + sh) {
              _scrollToEnd();
              apps.changeUnread(-1);

              /// 设置最新消息为已读
              await chatData(pageNum: 1, conversationId: widget.chatObj.id);
            } else {
              _state.value = _state.value.copy(unreadCount: _state.value.unreadCount + 1);
              widget.unreadCallback?.call(_state.value.unreadCount);
            }
          }
        }
      }
    } catch (e) {
      error('接收消息出错:$e');
    }
  }

  /// 输入框值
  void _contentChanged(String v) {
    if (v.isNotEmpty) {
      _state.value = _state.value.copy(contentIsText: true);
    } else {
      _state.value = _state.value.copy(contentIsText: false);
    }
  }

  /// 是否显示日期时间
  bool _needShowDate(int index) {
    if (_loadData.isEmpty || index == _loadData.length - 1) return false;
    return _loadData[index].needShowDate(_loadData[index + 1].createDate);
  }

  /// 滚动到底部
  void _scrollToEnd({Duration? duration}) {
    _controller.animateTo(
      _controller.position.minScrollExtent,
      duration: duration ?? const Duration(milliseconds: 300),
      curve: Curves.ease,
    );
  }

  /// 点击左下角新消息
  Future<void> _tapNewMsg() async {
    _controller.animateTo(
      _controller.position.minScrollExtent,
      duration: const Duration(milliseconds: 500),
      curve: Curves.ease,
    );

    apps.changeUnread(-_state.value.unreadCount);

    /// 设置最新消息为已读
    await chatData(pageNum: 1, conversationId: widget.chatObj.id);

    await delayed(const Duration(milliseconds: 500));
    if (_controller.offset > _controller.position.minScrollExtent) {
      _scrollToEnd();
    }
  }

  @override
  Widget build(BuildContext context) {
    return SafeScaffold(
      titleText: widget.chatObj.fromUserNickName,
      systemNavigationBarColor: _bgColor,
      tapToCloseKeyboard: true,
      backgroundColor: _bgColor,
      actions: <Widget>[
        if (_showGoods)
          IconButton(
            onPressed: () {
              _state.value = _state.value.copy(showGoods: !_state.value.showGoods);
            },
            icon: ExValueBuilder<_ChatState>(
              valueListenable: _state,
              shouldRebuild: (_ChatState p, _ChatState n) => p.showGoods != n.showGoods,
              builder: (_, _ChatState s, Widget? child) {
                return Icon(
                  IR.box2,
                  color: s.showGoods ? theme(context) : black(context).withOpacity(0.6),
                  size: 22,
                );
              },
            ),
          ),
      ],
      body: _isInit
          ? Stack(
              alignment: Alignment.bottomCenter,
              children: <Widget>[
                Padding(
                  padding: const EdgeInsets.only(bottom: 70),
                  child: NotificationListener<ScrollUpdateNotification>(
                    onNotification: (ScrollUpdateNotification s) {
                      if (s.metrics.pixels <= 0 && _state.value.unreadCount != 0) {
                        _state.value = _state.value.copy(unreadCount: 0);
                        widget.unreadCallback?.call(0);
                      }

                      if ((s.dragDetails?.delta.dy ?? 0) > 6) {
                        _focusNode.unfocus();
                        if (_state.value.showGoods) {
                          _state.value = _state.value.copy(showGoods: false);
                        }
                      }

                      if ((s.dragDetails?.delta.dy ?? 0) < 0) {
                        if (_state.value.showGoods) {
                          _state.value = _state.value.copy(showGoods: false);
                        }
                      }

                      return false;
                    },
                    child: CustomScrollView(
                      controller: _controller,
                      reverse: true,
                      physics: const ClampingScrollPhysics(),
                      center: _centerKey,
                      slivers: <Widget>[
                        SliverPadding(
                          key: _newChatKey,
                          padding: const EdgeInsets.symmetric(horizontal: 20),
                          sliver: SliverList(
                            delegate: SliverChildBuilderDelegate(
                              (BuildContext context, int index) {
                                return MsgItem(
                                  msgObj: _newData[index],
                                  myHeader: widget.chatObj.fromUserAvatarUrl,
                                  otherHeader: widget.chatObj.toUserAvatarUrl,
                                  showDate: false,
                                );
                              },
                              childCount: _newData.length,
                            ),
                          ),
                        ),
                        SliverPadding(
                          key: _centerKey,
                          padding: const EdgeInsets.symmetric(horizontal: 20),
                          sliver: SliverList(
                            delegate: SliverChildBuilderDelegate(
                              (BuildContext context, int index) {
                                if (_loadData.isEmpty) return const SizedBox.shrink();

                                if (index == _loadData.length) return _loading;

                                return MsgItem(
                                  msgObj: _loadData[index],
                                  myHeader: widget.chatObj.fromUserAvatarUrl,
                                  otherHeader: widget.chatObj.toUserAvatarUrl,
                                  showDate: _needShowDate(index) || _loadData.length - 1 == index,
                                );
                              },
                              childCount: _loadData.length + 1,
                            ),
                          ),
                        ),
                      ],
                    ),
                  ),
                ),
                if (_showGoods) _buildGoodsBox,
                _buildInputBox,
              ],
            )
          : const LoadingView(),
    );
  }

  /// 输入框
  Widget get _buildInputBox {
    return Column(
      mainAxisSize: MainAxisSize.min,
      crossAxisAlignment: CrossAxisAlignment.start,
      children: <Widget>[
        ExValueBuilder<_ChatState>(
          valueListenable: _state,
          builder: (_, _ChatState c, __) {
            return c.unreadCount == 0
                ? const SizedBox.shrink()
                : GestureDetector(
                    onTap: _tapNewMsg,
                    child: Container(
                      child: Text(
                        '收到 ${c.unreadCount} 条新消息',
                        style: TextStyle(
                          fontSize: 12,
                          color: theme(context),
                          fontWeight: FontWeight.bold,
                        ),
                      ),
                      padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10),
                      margin: const EdgeInsets.only(left: 20),
                      decoration: BoxDecoration(
                        color: white(context),
                        borderRadius: BorderRadius.circular(10).copyWith(
                          bottomLeft: const Radius.circular(2),
                        ),
                      ),
                    ),
                  );
          },
        ),
        Container(
          child: Row(
            children: <Widget>[
              Flexible(
                child: AppTextField(
                  onChanged: _contentChanged,
                  hintText: '发送私信...',
                  maxLength: 100,
                  height: 40,
                  controller: _textEditingController,
                  focusNode: _focusNode,
                  fillColor: white(context),
                  contentPadding: const EdgeInsets.all(12),
                  border: OutlineInputBorder(
                    borderSide: BorderSide.none,
                    borderRadius: BorderRadius.circular(6),
                  ),
                ),
              ),
              const SizedBox(width: 10),
              _sendBtn,
            ],
          ),
          color: _bgColor,
          padding: const EdgeInsets.all(20).copyWith(top: 10),
        ),
      ],
    );
  }

  /// 发送按钮
  Widget get _sendBtn {
    return ExValueBuilder<_ChatState>(
      valueListenable: _state,
      shouldRebuild: (_ChatState p, _ChatState n) => p.contentIsText != n.contentIsText || p.isSending != n.isSending,
      builder: (_, _ChatState s, __) {
        return AppBtn.animated(
          onTap: () => _send(s.contentIsText),
          child: s.isSending
              ? LoadingIcon(
                  size: 16,
                  color: white(context),
                  strokeWidth: 2,
                )
              : (s.contentIsText
                  ? Text(
                      '发送',
                      style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold, color: white(context)),
                    )
                  : Icon(Icons.add_rounded, size: 24, color: white(context))),
          radius: s.contentIsText ? 6 : 20,
          height: 40,
          width: s.contentIsText ? 60 : 40,
          alignment: Alignment.center,
          padding: EdgeInsets.zero,
        );
      },
    );
  }

  /// 物品信息
  Widget get _buildGoodsBox {
    return ExValueBuilder<_ChatState>(
      valueListenable: _state,
      shouldRebuild: (_ChatState p, _ChatState n) => p.showGoods != n.showGoods,
      child: SlideCallback(
        slideToTop: () => _state.value = _state.value.copy(showGoods: false),
        child: GoodsBox(
          id: widget.chatObj.goodsId,
          toUserId: widget.chatObj.fromUserId,
          toUserName: widget.chatObj.fromUserNickName,
          toUserHeader: widget.chatObj.fromUserAvatarUrl,
          showGoods: (bool s) {
            if (s != _showGoods) {
              safeSetState(() => _showGoods = s);
              if (!s)
                _state.value = _state.value.copy(showGoods: false);
              else
                _state.value = _state.value.copy(showGoods: true);
            }
          },
        ),
      ),
      builder: (_, _ChatState s, Widget? child) {
        return AnimatedPositioned(
          duration: const Duration(milliseconds: 300),
          curve: Curves.ease,
          top: s.showGoods ? 0 : -100,
          child: child!,
        );
      },
    );
  }

  /// 加载图标
  Widget get _loading {
    Widget load = Row(
      mainAxisAlignment: MainAxisAlignment.center,
      children: <Widget>[
        const LoadingIcon(size: 14, strokeWidth: 2),
        const SizedBox(width: 20),
        Text(
          '加载中...',
          style: TextStyle(color: disable(context), fontSize: 12),
        ),
      ],
    );

    if (_isNoMore)
      load = Row(
        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
          Icon(Icons.layers_rounded, color: disable(context), size: 22),
          const SizedBox(width: 20),
          Text(
            '没有更多了',
            style: TextStyle(color: disable(context), fontSize: 12),
          ),
        ],
      );
    else
      _onLoad();

    return SizedBox(
      height: 40,
      child: load,
    );
  }
}
